package org.xp.judgeplatform.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.xp.judgeplatform.common.PageResult;
import org.xp.judgeplatform.exception.BusinessException;
import org.xp.judgeplatform.mapper.*;
import org.xp.judgeplatform.converter.ProblemSetConverter;
import org.xp.judgeplatform.pojo.dto.ProblemSetDTO.*;
import org.xp.judgeplatform.pojo.entity.*;
import org.xp.judgeplatform.pojo.vo.ProblemSetVO.InviteCodeVO;
import org.xp.judgeplatform.pojo.vo.ProblemSetVO.ProblemSetMemberVO;
import org.xp.judgeplatform.pojo.vo.ProblemSetVO.ProblemSetVO;
import org.xp.judgeplatform.service.ProblemSetService;
import org.xp.judgeplatform.util.InviteCodeGenerator;
import org.xp.judgeplatform.util.RedisKeyConstants;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 题目集服务实现类
 */
@Slf4j
@Service
@AllArgsConstructor
public class ProblemSetServiceImpl implements ProblemSetService {

    private final ProblemSetMapper problemSetMapper;
    private final ProblemSetMemberMapper memberMapper;
    private final UserMapper userMapper;
    private final ProblemSetConverter problemSetConverter;
    private final StringRedisTemplate stringRedisTemplate;
    private final ProblemSetItemMapper problemSetItemMapper;

    /**
     * 创建题目集
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ProblemSetVO createProblemSet(ProblemSetCreateDTO createDTO, Integer userId) {
        log.info("创建题目集，用户ID: {}, 题目集名称: {}", userId, createDTO.getName());

        ProblemSet problemSet = new ProblemSet();
        problemSet.setName(createDTO.getName());
        problemSet.setDescription(createDTO.getDescription());
        problemSet.setCreatorId(userId);
        problemSet.setIsPublic(createDTO.getIsPublic());
        problemSet.setStatus("ACTIVE");
        problemSet.setMemberCount(1);
        problemSet.setProblemCount(0);
        problemSet.setInviteCode(generateUniqueInviteCode());

        problemSetMapper.insert(problemSet);

        createCreatorMember(problemSet.getId(), userId);

        if (createDTO.getProblemIds() != null && !createDTO.getProblemIds().isEmpty()) {
            addProblemsToSet(problemSet.getId(), createDTO.getProblemIds());
            problemSet.setProblemCount(createDTO.getProblemIds().size());
            problemSetMapper.updateById(problemSet);
        }

        return convertToVO(problemSet, userId);
    }

    /**
     * 查询题目集列表（用户视角）
     */
    @Override
    public PageResult<ProblemSetVO> getProblemSetList(ProblemSetQueryDTO queryDTO, Integer userId) {
        log.info("查询题目集列表，用户ID: {}", userId);

        IPage<ProblemSet> pageRequest = createPage(queryDTO.getPageNum(), queryDTO.getPageSize());
        LambdaQueryWrapper<ProblemSet> queryWrapper = new LambdaQueryWrapper<>();

        if (StringUtils.hasText(queryDTO.getKeyword())) {
            queryWrapper.like(ProblemSet::getName, queryDTO.getKeyword());
        }

        if ("MY".equals(queryDTO.getType())) {
            List<Long> userSetIds = getUserSetIds(userId);
            if (userSetIds.isEmpty()) {
                return new PageResult<>(List.of(), 0L, queryDTO.getPageNum(), queryDTO.getPageSize());
            }
            queryWrapper.in(ProblemSet::getId, userSetIds);
        } else {
            queryWrapper.eq(ProblemSet::getIsPublic, true);
        }

        queryWrapper.eq(ProblemSet::getStatus, "ACTIVE")
                    .orderByDesc(ProblemSet::getCreatedAt);

        IPage<ProblemSet> pageResult = problemSetMapper.selectPage(pageRequest, queryWrapper);
        List<ProblemSetVO> voList = pageResult.getRecords().stream()
                .map(problemSet -> convertToVO(problemSet, userId))
                .collect(Collectors.toList());

        return new PageResult<>(voList, pageResult.getTotal(), queryDTO.getPageNum(), queryDTO.getPageSize());
    }

    /**
     * 查询题目集列表（管理员视角）
     */
    @Override
    public PageResult<ProblemSetVO> getProblemSetListForAdmin(ProblemSetQueryDTO queryDTO) {
        log.info("管理员查询所有题目集");

        IPage<ProblemSet> pageRequest = createPage(queryDTO.getPageNum(), queryDTO.getPageSize());
        LambdaQueryWrapper<ProblemSet> queryWrapper = new LambdaQueryWrapper<>();

        if (StringUtils.hasText(queryDTO.getKeyword())) {
            queryWrapper.like(ProblemSet::getName, queryDTO.getKeyword());
        }

        if (StringUtils.hasText(queryDTO.getStatus())) {
            queryWrapper.eq(ProblemSet::getStatus, queryDTO.getStatus());
        }

        queryWrapper.orderByDesc(ProblemSet::getCreatedAt);

        IPage<ProblemSet> pageResult = problemSetMapper.selectPage(pageRequest, queryWrapper);
        List<ProblemSetVO> voList = pageResult.getRecords().stream()
                .map(problemSet -> convertToVO(problemSet, null))
                .collect(Collectors.toList());

        return new PageResult<>(voList, pageResult.getTotal(), queryDTO.getPageNum(), queryDTO.getPageSize());
    }

    /**
     * 获取题目集详情
     */
    @Override
    public ProblemSetVO getProblemSetDetail(Long setId, Integer userId) {
        log.info("获取题目集详情，ID: {}, 用户ID: {}", setId, userId);

        ProblemSet problemSet = problemSetMapper.selectById(setId);
        if (problemSet == null) {
            throw new BusinessException(404, "题目集不存在");
        }

        if (!problemSet.getIsPublic() && !isMember(setId, userId)) {
            throw new BusinessException(403, "无权访问该题目集");
        }

        ProblemSetVO vo = convertToVO(problemSet, userId);
        vo.setProblems(problemSetItemMapper.selectItemsWithProblemInfo(setId));

        return vo;
    }

    /**
     * 通过邀请码加入题目集
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void joinProblemSet(ProblemSetJoinDTO joinDTO, Integer userId) {
        String inviteCode = joinDTO.getInviteCode();
        log.info("用户 {} 尝试通过邀请码 {} 加入题目集", userId, inviteCode);

        ProblemSet problemSet = findProblemSetByInviteCode(inviteCode);

        if (problemSet == null) {
            throw new BusinessException(400, "邀请码无效或已过期");
        }

        if (!"ACTIVE".equals(problemSet.getStatus())) {
            throw new BusinessException(400, "该题目集已归档或未激活");
        }

        if (isMember(problemSet.getId(), userId)) {
            throw new BusinessException(400, "您已经是该题目集的成员");
        }

        ProblemSetMember member = new ProblemSetMember();
        member.setSetId(problemSet.getId());
        member.setUserId(userId);
        member.setRole("MEMBER");
        member.setJoinMethod("INVITE_CODE");
        member.setProgressCount(0);
        member.setTotalScore(0);
        member.setLastActiveAt(LocalDateTime.now());
        memberMapper.insert(member);

        problemSetMapper.updateMemberCount(problemSet.getId());
    }

    /**
     * 获取题目集成员列表
     */
    @Override
    public PageResult<ProblemSetMemberVO> getMembers(Long setId, Integer pageNum, Integer pageSize, Integer userId) {
        log.info("获取题目集 {} 的成员列表", setId);

        ProblemSet problemSet = problemSetMapper.selectById(setId);
        if (problemSet == null) {
            throw new BusinessException(404, "题目集不存在");
        }

        if (!problemSet.getIsPublic() && !isMember(setId, userId)) {
            throw new BusinessException(403, "无权查看成员列表");
        }

        IPage<ProblemSetMemberVO> pageRequest = createPage(pageNum, pageSize);
        IPage<ProblemSetMemberVO> pageResult = memberMapper.selectMembersWithUserInfo(pageRequest, setId);

        return new PageResult<>(pageResult.getRecords(), pageResult.getTotal(), pageNum, pageSize);
    }

    /**
     * 更新题目集信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ProblemSetVO updateProblemSet(Long setId, ProblemSetUpdateDTO updateDTO, Integer userId) {
        log.info("更新题目集 {}，操作用户: {}", setId, userId);

        ProblemSet problemSet = problemSetMapper.selectById(setId);
        if (problemSet == null) {
            throw new BusinessException(404, "题目集不存在");
        }

        if (!problemSet.getCreatorId().equals(userId)) {
            throw new BusinessException(403, "只有创建者可以更新题目集");
        }

        if (StringUtils.hasText(updateDTO.getName())) {
            problemSet.setName(updateDTO.getName());
        }
        if (updateDTO.getDescription() != null) {
            problemSet.setDescription(updateDTO.getDescription());
        }
        if (updateDTO.getIsPublic() != null) {
            problemSet.setIsPublic(updateDTO.getIsPublic());
        }
        if (StringUtils.hasText(updateDTO.getStatus())) {
            problemSet.setStatus(updateDTO.getStatus());
        }

        problemSetMapper.updateById(problemSet);

        return convertToVO(problemSet, userId);
    }

    /**
     * 删除题目集（软删除）
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteProblemSet(Long setId, Integer userId) {
        log.info("删除题目集 {}，操作用户: {}", setId, userId);

        ProblemSet problemSet = problemSetMapper.selectById(setId);
        if (problemSet == null) {
            throw new BusinessException(404, "题目集不存在");
        }

        if (!problemSet.getCreatorId().equals(userId)) {
            throw new BusinessException(403, "只有创建者可以删除题目集");
        }

        problemSet.setStatus("ARCHIVED");
        problemSetMapper.updateById(problemSet);
    }

    /**
     * 生成邀请码
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public InviteCodeVO generateInviteCode(Long setId, InviteCodeDTO dto, Integer userId) {
        log.info("为题目集 {} 生成邀请码，创建者: {}, 有效期: {}天", setId, userId, dto.getDays());

        ProblemSet problemSet = problemSetMapper.selectById(setId);
        if (problemSet == null) {
            throw new BusinessException(404, "题目集不存在");
        }

        if (!problemSet.getCreatorId().equals(userId)) {
            throw new BusinessException(403, "只有创建者可以生成邀请码");
        }

        String inviteCode = generateUniqueInviteCode();
        LocalDateTime expiresAt = null;
        boolean isPermanent = true;

        if (dto.getDays() != null && dto.getDays() > 0) {
            expiresAt = LocalDateTime.now().plusDays(dto.getDays());
            isPermanent = false;
        }

        problemSet.setInviteCode(inviteCode);
        problemSetMapper.updateById(problemSet);

        cacheInviteCode(inviteCode, setId, dto.getDays(), isPermanent);

        return new InviteCodeVO(inviteCode, expiresAt, isPermanent);
    }

    /**
     * 为题目集添加题目
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addProblemToSet(Long setId, ProblemSetAddItemDTO addItemDTO, Integer userId) {
        log.info("为题目集 {} 添加题目，创建者 {}", setId, userId);

        ProblemSet problemSet = problemSetMapper.selectById(setId);
        if (problemSet == null) {
            throw new BusinessException(404, "题目集不存在");
        }

        if (!problemSet.getCreatorId().equals(userId)) {
            throw new BusinessException(403, "只有创建者可以为题目集添加题目");
        }

        if (isProblemInSet(setId, addItemDTO.getProblemId())) {
            throw new BusinessException(400, "该题目已在题目集中,请勿重复添加");
        }

        if (addItemDTO.getSortOrder() == null) {
            addItemDTO.setSortOrder(getNextSortOrder(setId));
        }

        ProblemSetItem item = new ProblemSetItem();
        item.setSetId(setId);
        item.setProblemId(addItemDTO.getProblemId());
        item.setSortOrder(addItemDTO.getSortOrder());
        item.setIsRequired(true);
        item.setTargetScore(addItemDTO.getTargetScore() != null ? addItemDTO.getTargetScore() : 100);

        problemSetItemMapper.insert(item);
        problemSetMapper.updateProblemCount(setId);

        log.info("成功为题目集 {} 添加题目 {}", setId, addItemDTO.getProblemId());
    }

    /**
     * 创建分页对象
     */
    private <T> IPage<T> createPage(Integer pageNum, Integer pageSize) {
        return new Page<>(pageNum, pageSize);
    }

    /**
     * 生成唯一邀请码
     */
    private String generateUniqueInviteCode() {
        String inviteCode;
        do {
            inviteCode = InviteCodeGenerator.generate();
        } while (problemSetMapper.existsByInviteCode(inviteCode));
        return inviteCode;
    }

    /**
     * 创建创建者成员记录
     */
    private void createCreatorMember(Long setId, Integer userId) {
        ProblemSetMember creator = new ProblemSetMember();
        creator.setSetId(setId);
        creator.setUserId(userId);
        creator.setRole("CREATOR");
        creator.setJoinMethod("DIRECT_ADD");
        creator.setProgressCount(0);
        creator.setTotalScore(0);
        creator.setLastActiveAt(LocalDateTime.now());
        memberMapper.insert(creator);
    }

    /**
     * 批量添加题目到题目集
     */
    private void addProblemsToSet(Long setId, List<Long> problemIds) {
        if (problemIds == null || problemIds.isEmpty()) {
            return;
        }
        
        int sortOrder = 1;
        for (Long problemId : problemIds) {
            ProblemSetItem item = new ProblemSetItem();
            item.setSetId(setId);
            item.setProblemId(problemId);
            item.setSortOrder(sortOrder++);
            item.setIsRequired(true);
            item.setTargetScore(100);
            problemSetItemMapper.insert(item);
        }
        
        log.info("成功为题目集 {} 批量添加 {} 个题目", setId, problemIds.size());
    }

    /**
     * 获取用户加入的题目集ID列表
     */
    private List<Long> getUserSetIds(Integer userId) {
        return memberMapper.selectObjs(
                new LambdaQueryWrapper<ProblemSetMember>()
                        .eq(ProblemSetMember::getUserId, userId)
                        .select(ProblemSetMember::getSetId)
        ).stream()
         .map(obj -> (Long) obj)
         .collect(Collectors.toList());
    }

    /**
     * 通过邀请码查找题目集（优先从Redis缓存查询）
     */
    private ProblemSet findProblemSetByInviteCode(String inviteCode) {
        String redisKey = RedisKeyConstants.TEMP_INVITE_PREFIX + inviteCode;
        
        // 尝试从Redis获取
        String setIdStr = null;
        try {
            setIdStr = stringRedisTemplate.opsForValue().get(redisKey);
        } catch (Exception e) {
            log.warn("Redis查询失败，将从数据库查询, inviteCode={}", inviteCode, e);
        }
        
        // Redis命中，解析并查询
        if (setIdStr != null) {
            try {
                Long setId = Long.parseLong(setIdStr);
                ProblemSet problemSet = problemSetMapper.selectById(setId);
                if (problemSet != null) {
                    return problemSet;
                }
                // 缓存的ID对应的题目集不存在，清除缓存
                log.warn("Redis缓存的题目集不存在, inviteCode={}, setId={}", inviteCode, setId);
                try {
                    stringRedisTemplate.delete(redisKey);
                } catch (Exception ex) {
                    log.warn("清除无效缓存失败", ex);
                }
            } catch (NumberFormatException e) {
                log.error("Redis中的setId格式错误, inviteCode={}, setIdStr={}", inviteCode, setIdStr, e);
                try {
                    stringRedisTemplate.delete(redisKey);
                } catch (Exception ex) {
                    log.warn("清除错误缓存失败", ex);
                }
            } catch (Exception e) {
                log.error("查询题目集失败, setId={}", setIdStr, e);
            }
        }
        
        // Redis未命中或查询失败，从数据库查询
        try {
            ProblemSet problemSet = problemSetMapper.selectOne(
                    new LambdaQueryWrapper<ProblemSet>()
                            .eq(ProblemSet::getInviteCode, inviteCode)
            );
            
            // 查询成功且题目集存在，更新缓存
            if (problemSet != null) {
                try {
                    stringRedisTemplate.opsForValue().set(
                        redisKey, 
                        problemSet.getId().toString()
                    );
                } catch (Exception e) {
                    log.warn("更新Redis缓存失败, inviteCode={}", inviteCode, e);
                    // 缓存失败不影响主流程
                }
            }
            
            return problemSet;
        } catch (Exception e) {
            log.error("从数据库查询题目集失败, inviteCode={}", inviteCode, e);
            throw new BusinessException(500, "查询题目集失败，请稍后重试");
        }
    }

    /**
     * 缓存邀请码到Redis
     */
    private void cacheInviteCode(String inviteCode, Long setId, Integer days, boolean isPermanent) {
        String redisKey = RedisKeyConstants.TEMP_INVITE_PREFIX + inviteCode;
        
        try {
            if (isPermanent) {
                stringRedisTemplate.opsForValue().set(redisKey, setId.toString());
            } else {
                stringRedisTemplate.opsForValue().set(
                    redisKey, 
                    setId.toString(), 
                    days, 
                    TimeUnit.DAYS
                );
            }
            log.info("邀请码缓存成功, inviteCode={}, setId={}", inviteCode, setId);
        } catch (Exception e) {
            // Redis写入失败记录日志，但不影响主流程
            // 因为数据库中已经保存了inviteCode，即使缓存失败也能从数据库查询
            log.error("邀请码缓存失败, inviteCode={}, setId={}", inviteCode, setId, e);
        }
    }

    /**
     * 检查题目是否已在题目集中
     */
    private boolean isProblemInSet(Long setId, Long problemId) {
        return problemSetItemMapper.selectCount(
                new LambdaQueryWrapper<ProblemSetItem>()
                        .eq(ProblemSetItem::getSetId, setId)
                        .eq(ProblemSetItem::getProblemId, problemId)
        ) > 0;
    }

    /**
     * 获取下一个排序号
     */
    private Integer getNextSortOrder(Long setId) {
        QueryWrapper<ProblemSetItem> countWrapper = new QueryWrapper<>();
        countWrapper.eq("set_id", setId);
        Long count = problemSetItemMapper.selectCount(countWrapper);
        return count.intValue() + 1;
    }

    /**
     * 检查用户是否为题目集成员
     */
    private boolean isMember(Long setId, Integer userId) {
        return memberMapper.selectCount(
                new LambdaQueryWrapper<ProblemSetMember>()
                        .eq(ProblemSetMember::getSetId, setId)
                        .eq(ProblemSetMember::getUserId, userId)
        ) > 0;
    }

    /**
     * 转换为VO对象
     */
    private ProblemSetVO convertToVO(ProblemSet problemSet, Integer userId) {
        ProblemSetVO vo = problemSetConverter.toProblemSetVO(problemSet);

        User creator = userMapper.selectById(problemSet.getCreatorId());
        if (creator != null) {
            vo.setCreatorName(creator.getUsername());
        }

        if (userId != null) {
            ProblemSetMember member = memberMapper.selectOne(
                    new LambdaQueryWrapper<ProblemSetMember>()
                            .eq(ProblemSetMember::getSetId, problemSet.getId())
                            .eq(ProblemSetMember::getUserId, userId)
            );
            if (member != null) {
                vo.setUserRole(member.getRole());
            }
        }

        return vo;
    }
}
